package server

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"regexp"
	"runtime"
	"runtime/debug"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	log "github.com/sirupsen/logrus"
)

func ServerFixAllContainer(cli *client.Client, hostFilePath string) {
	// 使用 Docker client 获取所有容器的信息
	containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
	if err != nil {
		panic(err)
	}
	for _, container := range containers {
		ServerFixHostsByContainer(cli, hostFilePath, container.ID, "")
	}
}
func getHostsFile() string {
	switch runtime.GOOS {
	case "windows":
		return "C:\\Windows\\System32\\drivers\\etc\\hosts"
	case "linux":
		return "/etc/hosts"
	case "darwin":
		return "/private/etc/hosts"
	default:
		log.Warnf("Unknown OS %v", runtime.GOOS)
		return "/etc/hosts"
	}
	// return "/workspaces/GoProjects/dockerHosts/hosts"
}
func FixHosts(hostFilePath string, name string, ip string, isRun bool) {
	pattern := "^\\s*\\S+\\s+" + name + "\\s*#\\s*docker-hosts\\s*$"
	pattern2 := "^\\s*\\S+\\s+" + name + "\\s*.*$"
	newLine := fmt.Sprintf("%s %s # docker-hosts", ip, name)
	//读取文件内容
	file, err := os.OpenFile(hostFilePath, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		log.Errorf("Error open hosts file: %s", err)
		return
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	var lines []string
	found := false
	lineNum := 0
	for scanner.Scan() {
		data := scanner.Text()
		lineNum++
		matched, _ := regexp.MatchString(pattern, data)
		if matched {
			found = true
			//匹配替换
			if isRun {
				lines = append(lines, newLine)
				log.Printf("line %d update: %s", lineNum, newLine)
			} else {
				log.Printf("line %d delete: %s", lineNum, data)
			}
		} else {
			//原样输出
			lines = append(lines, data)
			matched2, _ := regexp.MatchString(pattern2, data)
			if matched2 {
				log.Warnf("line %d ignore: %s", lineNum, data)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		log.Errorf("Error scan hosts file: %s", err)
		return
	}
	file.Seek(0, 0)
	file.Truncate(0)

	w := bufio.NewWriter(file)
	for _, line := range lines {
		fmt.Fprintln(w, line)
	}
	if !found && isRun {
		//未找到匹配行， 新增行
		fmt.Fprintln(w, newLine)
		log.Printf("line %d append: %s", lineNum, newLine)
	}
	errs := w.Flush()
	if errs != nil {
		log.Errorf("Error flush hosts file: %s", errs)
		return
	}
}

func ServerFixHostsByContainer(cli *client.Client, hostFilePath string, id string, action string) {
	container, err := cli.ContainerInspect(context.Background(), id)
	name := container.Name
	state := container.State.Status
	networkMode := container.HostConfig.NetworkMode
	if name[0:1] == "/" {
		name = name[1:]
	}
	if err != nil {
		log.Errorf("Error inspect container: %s", err)
		return
	}
	networks := container.NetworkSettings.Networks
	for _, v := range networks {
		ip := v.IPAddress
		log.Printf("Fixing hosts for container: %s, state: %s, networkMode: %s, ip: %s, action: %s", name, state, networkMode, ip, action)
		FixHosts(hostFilePath, name, ip, (state == "running" && ip != "" && networkMode != "host" && action != "die"))
	}
}
func ServerHandler() {
	hostFilePath := getHostsFile()
	log.Printf("Starting server, hosts file: %s", hostFilePath)
	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
	if err != nil {
		log.Errorf("Error create docker client: %s", err)
		return
	}
	ServerFixAllContainer(cli, hostFilePath)
	msgs, errs := cli.Events(context.Background(), types.EventsOptions{})
	for {
		select {
		case err := <-errs:
			log.Errorf("Docker error event: %v", err)
			return
		case msg := <-msgs:
			if msg.Type == "container" {
				action := msg.Action
				if action == "start" || action == "die" {
					id := msg.Actor.ID
					ServerFixHostsByContainer(cli, hostFilePath, id, string(action))
				}
			}
		}
	}
}
func ServerHandleError() {
	r := recover()
	if r != nil {
		log.Errorf("%s", debug.Stack())
		log.Errorln("程序异常中断，5秒后重启...")
		time.Sleep(5 * time.Second)
		StartServer()
	}
}

func StartServer() {
	defer ServerHandleError()
	delay := int64(2)
	for {
		ServerHandler()
		log.Errorf("程序异常，%d秒后重启...", delay)
		time.Sleep(time.Duration(delay) * time.Second)
		delay += 2
		if delay > int64((time.Duration(5) * time.Minute).Seconds()) {
			delay = int64(2)
		}
	}
}
